package com.ikingtech.framework.sdk.workbench.embedded.aspect;

import com.ikingtech.framework.sdk.base.model.BatchParam;
import com.ikingtech.framework.sdk.context.security.Me;
import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.framework.sdk.workbench.api.TodoTaskApi;
import com.ikingtech.framework.sdk.workbench.embedded.annotation.Todo;
import com.ikingtech.framework.sdk.workbench.model.TodoTaskDTO;
import com.ikingtech.framework.sdk.workbench.model.TodoTaskDeleteBatchParamDTO;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @author tie yan
 */
@Aspect
@RequiredArgsConstructor
public class TodoAspect {

    private final TodoTaskApi api;

    private static final SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();

    @AfterReturning(pointcut = "@annotation(todo)", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Todo todo, Object result) {
        EvaluationContext context = new StandardEvaluationContext();
        if (null != result) {
            context.setVariable("_res", result);
        }
        if (joinPoint.getArgs().length > 0) {
            for (int i = 0; i < joinPoint.getArgs().length; i++) {
                context.setVariable(Tools.Str.format("_param{}", i), joinPoint.getArgs()[i]);
            }
        }
        List<String> appendUserIds = new ArrayList<>();
        List<String> completeUserIds = new ArrayList<>();
        if (todo.appendUser().loginUser()) {
            appendUserIds.add(Me.id());
        } else {
            appendUserIds.addAll(this.parseUser(context, todo.appendUser().userId()));
        }
        if (todo.completeUser().loginUser()) {
            completeUserIds.add(Me.id());
        } else {
            if (Tools.Str.isNotBlank(todo.completeUser().userId())) {
                completeUserIds.addAll(this.parseUser(context, todo.completeUser().userId()));
            }
        }
        String businessName = todo.businessName();
        if (businessName.contains("_res") || businessName.contains("_param")) {
            businessName = (String) Objects.requireNonNull(EXPRESSION_PARSER.parseExpression(businessName).getValue(context));
        }
        String summary = todo.summary();
        if (summary.contains("_res") || summary.contains("_param")) {
            summary = (String) Objects.requireNonNull(EXPRESSION_PARSER.parseExpression(summary).getValue(context));
        }
        String redirectLinkParam = todo.redirect().param();
        if (redirectLinkParam.contains("_res") || redirectLinkParam.contains("_param")) {
            redirectLinkParam = (String) Objects.requireNonNull(EXPRESSION_PARSER.parseExpression(redirectLinkParam).getValue(context));
        }
        String businessId = todo.businessId();
        if (businessId.contains("_res") || businessId.contains("_param")) {
            businessId = (String) Objects.requireNonNull(EXPRESSION_PARSER.parseExpression(businessId).getValue(context));
        }
        TodoTaskDeleteBatchParamDTO deleteParam = new TodoTaskDeleteBatchParamDTO();
        deleteParam.setBusinessId(businessId);
        deleteParam.setUserIds(completeUserIds);
        this.api.deleteBatch(deleteParam);
        List<TodoTaskDTO> newTasks = new ArrayList<>();
        for (String appendUserId : appendUserIds) {
            TodoTaskDTO task = new TodoTaskDTO();
            task.setBusinessName(businessName);
            task.setBusinessId(businessId);
            task.setSummary(summary);
            task.setStatus(todo.status());
            task.setRedirect(Tools.Str.format("{}/{}", todo.redirect().link(), redirectLinkParam));
            task.setUserId(appendUserId);
            task.setAppCode(todo.appCode());
            newTasks.add(task);
        }
        this.api.addBatch(BatchParam.build(newTasks));
    }

    private List<String> parseUser(EvaluationContext context, String userIdsExpression) {
        if (userIdsExpression.contains("_res") || userIdsExpression.contains("_param")) {
            Object userIds = Objects.requireNonNull(EXPRESSION_PARSER.parseExpression(userIdsExpression).getValue(context));
            if (userIds instanceof List) {
                return Tools.Obj.list(userIds);
            }
            if (userIds instanceof String) {
                return Tools.Str.split((String) userIds);
            }
            return new ArrayList<>();
        } else {
            return Tools.Str.split(userIdsExpression);
        }
    }
}
